package dal

import (
	"context"
	"time"

	"gorm.io/plugin/dbresolver"

	"code.byted.org/bytertc/whiteboard_server/biz/dal/ddl"
	"code.byted.org/bytertc/whiteboard_server/biz/dal/spec"
	"code.byted.org/bytertc/whiteboard_server/biz/pkg/biz_error"
	"github.com/pkg/errors"
)

func (r *repo) CreateTranscodeTask(ctx context.Context, task *ddl.TranscodeTask) error {
	err := r.db.WithContext(ctx).Table(ddl.TableTranscodeTask).Create(task).Error
	if IsDuplicateError(err) {
		return biz_error.Conflict.Wrap(errors.WithStack(err))
	}
	return err
}

func (r *repo) GetTranscodeTask(ctx context.Context, unique spec.TranscodeTaskUnique) (*ddl.TranscodeTask, error) {
	var t *ddl.TranscodeTask
	err := r.db.WithContext(ctx).Clauses(dbresolver.Write).Table(ddl.TableTranscodeTask).Where("app_id=? and task_id = ?", unique.GetAppId(), unique.GetTaskId()).First(&t).Error
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return t, nil
}

func (r *repo) GetTranscodeTaskByFileID(ctx context.Context, fileID string) (*ddl.TranscodeTask, error) {
	var t *ddl.TranscodeTask
	err := r.db.WithContext(ctx).Clauses(dbresolver.Write).Table(ddl.TableTranscodeTask).Where("file_id = ?", fileID).First(&t).Error
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return t, nil
}

func (r *repo) UpdateTranscodeTask(ctx context.Context, unique spec.TranscodeTaskUnique, ups map[string]interface{}) error {
	err := r.db.WithContext(ctx).Table(ddl.TableTranscodeTask).Where("app_id=? and task_id = ?", unique.GetAppId(), unique.GetTaskId()).Updates(ups).Error
	return err
}

func (r *repo) BatchCreateTranscodeResult(ctx context.Context, results []*ddl.TranscodeResult) error {
	err := r.db.WithContext(ctx).Table(ddl.TableTranscodeResult).CreateInBatches(results, 512).Error
	if IsDuplicateError(err) {
		return biz_error.Conflict.Wrap(errors.WithStack(err))
	}
	return err
}

func (r *repo) GetTranscodeResult(ctx context.Context, unique spec.TranscodeTaskUnique) ([]*ddl.TranscodeResult, error) {
	var rs []*ddl.TranscodeResult
	err := r.db.WithContext(ctx).Clauses(dbresolver.Write).Table(ddl.TableTranscodeResult).Where("app_id=? and task_id = ?", unique.GetAppId(), unique.GetTaskId()).Find(&rs).Error
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return rs, nil
}

func (r *repo) GetExceptionStaticTask(ctx context.Context) ([]*ddl.TranscodeTask, error) {
	var rs []*ddl.TranscodeTask
	err := r.db.WithContext(ctx).Table(ddl.TableTranscodeTask).Where("transcode_mode = 0 and status = 1 and cur_pages < total_pages and update_time < ?", time.Now().Add(-10*time.Second).Format("2006-01-02 15:04:05")).Find(&rs).Error
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return rs, nil
}

func (r *repo) GetExceptionDynamicTask(ctx context.Context) ([]*ddl.TranscodeTask, error) {
	var rs []*ddl.TranscodeTask
	err := r.db.WithContext(ctx).Table(ddl.TableTranscodeTask).Where("transcode_mode = 1 and status = 1 and create_time < ?", time.Now().Add(-time.Minute).Format("2006-01-02 15:04:05")).Find(&rs).Error
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return rs, nil
}

func (r *repo) UpdateExceptionDynamicTask(ctx context.Context, taskIDs []string) error {
	return r.db.WithContext(ctx).Table(ddl.TableTranscodeTask).Where("task_id in ?", taskIDs).Updates(map[string]interface{}{
		"status":   3,
		"err_code": 500,
		"err_msg":  "timeout",
	}).Error
}

func (r *repo) UpdateExceptionStaticTask(ctx context.Context, taskIDs []string) error {
	return r.db.WithContext(ctx).Table(ddl.TableTranscodeTask).Where("task_id in ?", taskIDs).Updates(map[string]interface{}{
		"status":   3,
		"err_code": 500,
		"err_msg":  "timeout",
	}).Error
}

func (r *repo) LockStaticMonitor(ctx context.Context) error {
	expire := 800 * time.Millisecond
	ok, err := r.cache.SetNX(ctx, genStaticMonitorKey(), "static", expire)
	if err != nil {
		return biz_error.CacheError.Wrap(errors.WithStack(err))
	}
	if ok {
		return nil
	}
	return errors.New("setnx conflict")
}

func (r *repo) LockDynamicMonitor(ctx context.Context) error {
	expire := 5 * time.Second
	ok, err := r.cache.SetNX(ctx, genDynamicMonitorKey(), "dynamic", expire)
	if err != nil {
		return biz_error.CacheError.Wrap(errors.WithStack(err))
	}
	if ok {
		return nil
	}
	return errors.New("setnx conflict")
}

func (r *repo) LockTranscodeTask(ctx context.Context, taskID string) error {
	expire := 2 * time.Second
	ok, err := r.cache.SetNX(ctx, genTaskMonitorKey(taskID), "transcode_task", expire)
	if err != nil {
		return biz_error.CacheError.Wrap(errors.WithStack(err))
	}
	if ok {
		return nil
	}
	return errors.New("setnx conflict")
}

func genStaticMonitorKey() string {
	return "static_transcode_monitor"
}

func genDynamicMonitorKey() string {
	return "dynamic_transcode_monitor"
}

func genTaskMonitorKey(taskID string) string {
	return "transcode_task:" + taskID
}
